package com.sheepone.distributor.config;

import com.sheepone.distributor.Distributor;
import com.sheepone.distributor.WorkerStation;
import com.sheepone.distributor.WorkerStationManager;
import com.sheepone.distributor.exeception.MultipleListenersForTopic;
import com.sheepone.distributor.listener.StationEventListener;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.List;

/**
 * @author Miss.杨
 * @description
 * @since 2024/2/15 - 16:22
 */
public class DistributorXmlLoader {

    private static final String CONFIGURATION_ELEMENT = "configuration";

    public static Distributor loadXml(String configPath) throws DocumentException {
        // 创建SAXReader对象
        SAXReader reader = new SAXReader();
        // 读取XML文件
        Document document = reader.read(new File(configPath));
        // 获取根元素
        Element configuration = document.getRootElement();

        if (!CONFIGURATION_ELEMENT.equals(configuration.getName())) {
            throw new DocumentException("Invalid configuration file");
        }

        // 处理WorkerStation配置
        Element workerStations = configuration.element("worker-stations");
        List<WorkerStationConfig> workerStationConfigs = loadWorkerStation(workerStations);
        WorkerStationManager.INSTANCE.registerWorkerStation(workerStationConfigs);

        // 处理Distributor配置
        Element distributorLine = configuration.element("distributor-line");
        DistributorLineConfig distributorLineConfig = loadDistributorLine(distributorLine);
        Distributor distributor = DistributorCreator.create(distributorLineConfig);

        // 处理StationEventListener
        Element listenersElement = configuration.element("station-listeners");
        loadStationEventListener(listenersElement);

        // 启动Distributor
        return distributor.start();
    }

    private static DistributorLineConfig loadDistributorLine(Element distributorLine) {
        DistributorLineConfig.DistributorLineConfigXmlBuilder builder = DistributorLineConfig.builder()
                .name(distributorLine.attributeValue("name"))
                .size(distributorLine.attributeValue("size"))
                .handlerNum(distributorLine.attributeValue("handler-num"));

        Element intervalTimeElement = distributorLine.element("interval-time");
        Element delayElement = distributorLine.element("enableDelay");
        Element listenersListElement = distributorLine.element("listeners");
        if (listenersListElement != null) {
            List<Element> listenersElement = listenersListElement.elements("listener");
            String[] listeners = new String[listenersElement.size()];
            int i = 0;
            for (Element listener : listenersElement) {
                listeners[i++] = listener.getText();
            }
            builder.listeners(listeners);
        }
        if (intervalTimeElement != null) {
            builder.intervalTime(intervalTimeElement.getText());
        }
        if (delayElement != null) {
            builder.enableDelay(Boolean.parseBoolean(delayElement.getText()));
        }
        return builder.build();
    }

    private static List<WorkerStationConfig> loadWorkerStation(Element workerStations) {
        List<WorkerStationConfig> configs = workerStations.elements("station").stream().map(station -> WorkerStationConfig.builder()
                .station(station.attributeValue("name"))
                .lineSize(station.attributeValue("line-size"))
                .handlerNum(station.attributeValue("handler-num"))
                .keepAliveTime(station.attributeValue("keep-alive-time"))
                .loadFactor(station.attributeValue("load-factor"))
                .build()).toList();
        return configs;
    }

    private static void loadStationEventListener(Element listenersElement) {
        List<Element> listeners = listenersElement.elements("listener");
        WorkerStationManager workerLineManager = WorkerStationManager.INSTANCE;
        listeners.forEach(listenerElement -> {
            String className = listenerElement.getText();
            StationEventListener<?> eventListener;
            try {
                eventListener = (StationEventListener<?>) Class.forName(className).getConstructor().newInstance();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            String station = listenerElement.attributeValue("station");
            WorkerStation workerStation = workerLineManager.getWorkerStation(station);
            String topic = listenerElement.attributeValue("topic");
            if (!workerStation.register(topic, eventListener)) {
                throw new MultipleListenersForTopic(station, topic);
            }
        });
    }


}
